//
//  AirBSDSocketListener.h
//  AirCpp
//
//  Created by Penghao on 14-2-25.
//  Copyright (c) 2014年 PengHao. All rights reserved.
//

#ifndef __AirCpp__AirBSDSocketListener__
#define __AirCpp__AirBSDSocketListener__

#include <iostream>
#include "AirBSDSocket.h"

namespace AirCpp{
    template <class CallBack>
    class AirBSDSocketListener : public AirBSDSocket<CallBack> {
    public:
        unsigned int max_num_in_queue;
        unsigned long max_connection_num;
        unsigned long current_connection_num;
        bool is_starting;
    public:
        
        void callback(int *socket_handle, CALL_BACK_TYPE_SOCKET type, void *value){
            if (NULL != this->callback_target) {
                switch (type) {
                    case CALL_BACK_TYPE_SOCKET_RESEIVED_CONN_REQUEST:
                        AirSocket<CallBack>::callback_target->handle_connect_request(socket_handle, (sockaddr *)value);
                        break;
                    default:
                        break;
                }
            }
        }
        
        AirBSDSocketListener(CallBack *call_back):current_connection_num(0),AirBSDSocket<CallBack>(call_back){
            is_starting = false;
            max_num_in_queue = 10;
        }
        /**
         portnum, 一个监听的接口.
         backlog, 一个决定监听队列大小的整数，当有一个连接请求到来，就会进入此监听队列，当队列满后，新的连接请求会返回错误。
         */
        int listen_port(unsigned short portnum, unsigned int backlog)
        {
            if (AirBSDSocket<CallBack>::socketHandle<0) {
                DebugLog("socket isnot init,please init before! \n");
                return -1;
            }
            AirBSDSocket<CallBack>::hp = AirBSDSocket<CallBack>::get_host_info();
            if (AirBSDSocket<CallBack>::hp == NULL) {
                DebugLog("failed to get gethostbyname\n");
                return -1;
            }
            AirBSDSocket<CallBack>::target_addr->sin_family= AirBSDSocket<CallBack>::hp->h_addrtype;                     /* this is our host address */
            AirBSDSocket<CallBack>::target_addr->sin_port= htons(portnum);                       /* this is our port number */
            if (0 != bind(AirBSDSocket<CallBack>::socketHandle, (const struct sockaddr *)(AirBSDSocket<CallBack>::target_addr), sizeof(const struct sockaddr))) {
                perror("failed to bind socket\n");
                close(AirBSDSocket<CallBack>::socketHandle);
                return(-1);                                             /* bind address to socket */
            }
            if (listen(AirBSDSocket<CallBack>::socketHandle, backlog)<0) {
                perror("listen socket failed\n");
                return AirBSDSocket<CallBack>::socketHandle;
            }else{
                max_num_in_queue = backlog;
            }
            return(AirBSDSocket<CallBack>::socketHandle);
        }
        
        int start_listen(CallBack *_target){
            if (AirBSDSocket<CallBack>::socketHandle < 0) {
                DebugLog("this socketHandle havn't be init");
                return -1;
            }
            DebugLog("start listen socketHandle %d", AirBSDSocket<CallBack>::socketHandle);
            
            is_starting = true;
            int  t;
            sockaddr client_addr;
            socklen_t client_size;
            AirBSDSocket<CallBack>::callback_target = _target;
            while (is_starting) {
                if ((t= accept_connection(&client_addr, &client_size)) < 0) { /* get a connection */
                    if (errno == EINTR)             /* EINTR might happen on accept(), */
                        continue;                   /* try again */
                    else
                        return -1;
                }
                callback(&t,  CALL_BACK_TYPE_SOCKET_RESEIVED_CONN_REQUEST, &client_addr);
                sleep(1.0);
            }
            return 0;
        };
        
        void max_connection(unsigned long number){
            max_connection_num = number;
        }
        void stop_listen(){
            is_starting = false;
            AirBSDSocket<CallBack>::close_connect();
        }
        
        int accept_connection(sockaddr *client_addr, socklen_t *client_size)
        {
            int t = -1;                                                          /* socket of connection */
            if (current_connection_num >= max_connection_num) {
                t = -2;
            }else if ((t = accept(AirBSDSocket<CallBack>::socketHandle, client_addr, client_size)) < 0){
                t = -1;
            }
            callback(&t,  CALL_BACK_TYPE_SOCKET_RESEIVED_CONN_REQUEST, &client_addr);
            return(t);
        }
        
        
    };
}

#endif /* defined(__AirCpp__AirBSDSocketListener__) */
